package main

import (
	"context"
	"fmt"
	"time"
)

func worker(ctx context.Context, id int) {
	for {
		select {
		case <-ctx.Done(): // ① 监听取消信号
			fmt.Printf("worker %d: %v, exit\n", id, ctx.Err())
			return
		default:
			fmt.Printf("worker %d: working...\n", id)
			time.Sleep(500 * time.Millisecond)
		}
	}
}

func main() {
	// ② 创建可取消的上下文
	ctx, cancel := context.WithCancel(context.Background())

	// ③ 启动 3 个 goroutine，都传同一个 ctx
	for i := 1; i <= 3; i++ {
		go worker(ctx, i)
	}

	time.Sleep(2 * time.Second)
	fmt.Println("main: 发送取消信号")
	cancel() // ④ 广播取消

	time.Sleep(500 * time.Millisecond) // 等它们打印退出日志
}

// `ctx.Done()` | 只读通道 `<-chan struct{}`；**关闭即代表取消**。                               |
// `ctx.Err()`  | 返回 `context.Canceled` 或 `context.DeadlineExceeded`，告诉你**为什么被取消**。 |
// `cancel()`   | **调用一次**就关闭 `Done()`，**线程安全、幂等**（多次调用无副作用）。                       |

// 超时取消
// ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)到点自动调用 cancel()，内部同样是关闭 Done()。
// 截止时间取消
// ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(3*time.Second))
// 值传递（非信号，仅携带数据）
// ctx := context.WithValue(parent, key, value)

// 使用守则（官方推荐）
// 函数入口第一个参数永远是 context.Context；
// 不要把 context.Context 放结构体字段，要显式传参；
// cancel() 必须由创建者负责调用，防止泄漏；
// 监听方只读 Done()，永远不要自己关闭它。
